﻿using MvdXmlParse;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Windows;
using Vision.ModuleTool;
using Vision.VControl;
using Vision.VHelper;
using VisionDesigner;
using VisionDesigner.CircleFind;
using MessageBox = System.Windows.MessageBox;

namespace Vision.Modules
{
    /// <summary>
    /// CircleFind.xaml 的交互逻辑
    /// </summary>
    public partial class CircleFind : Window, IModule
    {
        #region Private Filed

        public string ModuleName => "圆查找";                              // 模块名
        public DisplayCt DisplayManager { get; set; } = new DisplayCt();   // 显示模块
        public ModuleInput Input { get; set; }
        public ModuleOutput Output { get; set; }

        public MVDRenderActivex _mVDRender;                                // 显示面板

        private CMvdImage _InputImage;                                     // 输入图像数据
        public CMvdImage InputData
        {
            get => _InputImage;
            set
            {
                _InputImage = value;
                OnInputDataChanged();
            }
        }
        public int OutputData { get; set; }                             // 输出数据(暂无)

        CCircleFindTool _CircleFindTool = null;                         // 圆查找工具
        CMvdXmlParseTool _XmlParseTool = null;                          // 运行参数解析工具
        Stopwatch _StopWatch = new Stopwatch();                         // 计时工具
        private ObservableCollection<ParamData> Items { get; set; }      // 配置参数列表


        // 获取处理结果
        public CCircleFindResult Result = new CCircleFindResult();      // 圆查找结果

        #endregion

        #region Function

        public CircleFind()
        {
            InitializeComponent();
            InitMVDRender();
            Items = new ObservableCollection<ParamData>();
            // 初始化参数界面
            InitParam();
        }

        /// <summary>
        /// 显示控件初始化
        /// </summary>
        private void InitMVDRender()
        {
            _mVDRender = DisplayManager.mVDRender;
            DisplayManager.SupportedROIs = new List<ROIType>
            {
                ROIType.全图,
                ROIType.矩形,
                ROIType.圆,
                ROIType.扇环,
                ROIType.多边形
            };
            DisplayManager.UpdateROIButtons();
            DisplayManager.SupportedMaskROIs = new List<ROIType>
            {
                ROIType.多边形
            };
            DisplayManager.UpdateMaskROIButtons();
            DisplayManager.UpdateVisibleButtons(new List<string> { "圆弧", "卡尺", "轮廓" });
            display.Children.Add(DisplayManager);
        }

        /// <summary>
        /// 参数初始化
        /// </summary>
        public void InitParam()
        {
            try
            {
                _CircleFindTool = new CCircleFindTool();
                byte[] runParamConfigBytes = new byte[256];
                uint runParamConfigDataSize = 256;
                uint runParamConfigDataLength = 0;

                try
                {
                    _CircleFindTool.SaveConfiguration(runParamConfigBytes, runParamConfigDataSize, ref runParamConfigDataLength);
                }
                catch (MvdException ex)
                {
                    if (MVD_ERROR_CODE.MVD_E_NOENOUGH_BUF == ex.ErrorCode)
                    {
                        runParamConfigBytes = new byte[runParamConfigDataLength];
                        runParamConfigDataSize = runParamConfigDataLength;
                        _CircleFindTool.SaveConfiguration(runParamConfigBytes, runParamConfigDataSize, ref runParamConfigDataLength);
                    }
                    else
                    {
                        throw ex;
                    }
                }
                UpdateParamList(runParamConfigBytes, runParamConfigDataLength);

            }
            catch (MvdException ex)
            {
                MessageBox.Show(String.Format("Fail to initialize the tool, Module : {0}, ErrorCode : 0x{1}, Message : {2}.\r\n", ex.ModuleType.ToString(), ex.ErrorCode.ToString("X"), ex.Message));
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(String.Format("Fail to initialize the tool, Message : {0}, StackTrace : {1}.\r\n", ex.Message, ex.StackTrace));
            }
        }
        /// <summary>
        /// 配置参数页面
        /// </summary>
        /// <param name="bufXml"></param>
        /// <param name="nXmlLen"></param>
        private void UpdateParamList(Byte[] bufXml, uint nXmlLen)
        {

            if (null == _XmlParseTool)
            {
                _XmlParseTool = new CMvdXmlParseTool(bufXml, nXmlLen);
            }
            else
            {
                _XmlParseTool.UpdateXmlBuf(bufXml, nXmlLen);
            }

            Items.Clear();

            // Fill IntValueList
            foreach (var intValue in _XmlParseTool.IntValueList)
            {
                Items.Add(new IntParamData
                {
                    Name = intValue.Name,
                    DisplayName = intValue.DisplayName,
                    Value = intValue.CurValue,
                });
            }

            // Fill EnumValueList
            foreach (var enumValue in _XmlParseTool.EnumValueList)
            {
                Items.Add(new EnumParamData(
                    enumValue.EnumRange.Select(ev => (ev.DisplayName, ev.Name)))
                {
                    Name = enumValue.Name,
                    DisplayName = enumValue.DisplayName,
                    Value = enumValue.CurValue.DisplayName
                });
            }

            foreach (var item in Items)
            {
                item.ValueChanged += ParamData_ValueChanged;
            }

            dataGrid.ItemsSource = Items;

        }

        /// <summary>
        /// 处理 InputData 设置时的初始化逻辑的方法
        /// </summary>
        private void OnInputDataChanged()
        {
            // 在这里添加你的初始化逻辑
            // 例如，初始化工具，处理图像等
            if (_InputImage != null)
            {
                if (MVD_PIXEL_FORMAT.MVD_PIXEL_RGB_RGB24_C3 == _InputImage.PixelFormat
                 || MVD_PIXEL_FORMAT.MVD_PIXEL_RGB_RGB24_P3 == _InputImage.PixelFormat)
                {
                    _InputImage.ConvertImagePixelFormat(MVD_PIXEL_FORMAT.MVD_PIXEL_MONO_08);
                }
                //// 显示控件读取图像
                _mVDRender.LoadImageFromObject(_InputImage);
                _mVDRender.Display();
            }
        }

        /// <summary>
        /// 保存Xml参数
        /// </summary>
        /// <param name="filePath"></param>
        private void SaveXmlFile(string filePath)
        {
            try
            {
                using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
                {
                    byte[] runParamConfigBytes = new byte[256];
                    uint runParamConfigDataSize = 256;
                    uint runParamConfigDataLength = 0;
                    try
                    {
                        _CircleFindTool.SaveConfiguration(runParamConfigBytes, runParamConfigDataSize, ref runParamConfigDataLength);
                    }
                    catch (MvdException ex)
                    {
                        if (MVD_ERROR_CODE.MVD_E_NOENOUGH_BUF == ex.ErrorCode)
                        {
                            runParamConfigBytes = new byte[runParamConfigDataLength];
                            runParamConfigDataSize = runParamConfigDataLength;
                            _CircleFindTool.SaveConfiguration(runParamConfigBytes, runParamConfigDataSize, ref runParamConfigDataLength);
                        }
                        else
                        {
                            throw ex;
                        }
                    }

                    fileStream.Write(runParamConfigBytes, 0, Convert.ToInt32(runParamConfigDataLength));
                    fileStream.Flush();

                    Logger.Log("Finish exporting xml file.\r\n");
                }
            }
            catch (MvdException ex)
            {
                Logger.Log(String.Format("Fail to export xml file, Module : {0}, ErrorCode : 0x{1}, Message : {2}.\r\n", ex.ModuleType.ToString(), ex.ErrorCode.ToString("X"), ex.Message));
            }
            catch (System.Exception ex)
            {
                Logger.Log(String.Format("Fail to export xml file, Message : {0}, StackTrace : {1}.\r\n", ex.Message, ex.StackTrace));
            }
        }

        /// <summary>
        /// 导入Xml参数
        /// </summary>
        /// <param name="filePath"></param>
        private void ImportXmlFile(string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    byte[] runParamConfigBytes = null;
                    uint runParamConfigDataLength = 0;

                    using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                    {
                        runParamConfigBytes = new byte[fileStream.Length];
                        runParamConfigDataLength = Convert.ToUInt32(fileStream.Read(runParamConfigBytes, 0, runParamConfigBytes.Length));
                    }

                    _CircleFindTool.LoadConfiguration(runParamConfigBytes, runParamConfigDataLength);
                    UpdateParamList(runParamConfigBytes, runParamConfigDataLength);
                    Logger.Log("Finish importing xml file.\r\n");
                }
            }
            catch (MvdException ex)
            {
                Logger.Log(String.Format("Fail to import xml file, Module : {0}, ErrorCode : 0x{1}, Message : {2}.\r\n", ex.ModuleType.ToString(), ex.ErrorCode.ToString("X"), ex.Message));
            }
            catch (System.Exception ex)
            {
                Logger.Log(String.Format("Fail to import xml file, Message : {0}, StackTrace : {1}.\r\n", ex.Message, ex.StackTrace));
            }
        }

        /// <summary>
        /// 运行圆查找模块
        /// </summary>
        public void Run()
        {
            try
            {
                if ((null == _CircleFindTool) || (null == _InputImage))
                {
                    throw new MvdException(MVD_MODULE_TYPE.MVD_MODUL_APP, MVD_ERROR_CODE.MVD_E_CALLORDER);
                }

                // 清理绘制结果
                DisplayManager.Clear();

                // 算法处理
                _CircleFindTool.InputImage = _InputImage;
                _CircleFindTool.ROI = DisplayManager.ROIData as CMvdShape;
                _CircleFindTool.ClearMasks();
                foreach (var item in DisplayManager._MaskShapeList)
                {
                    _CircleFindTool.AddMask(item);
                }

                _StopWatch.Restart();
                _CircleFindTool.Run();
                _StopWatch.Stop();
                Logger.Log(String.Format("Running cost: {0}.\r\n", _StopWatch.ElapsedMilliseconds.ToString("#0.000")));

                Result = _CircleFindTool.Result;
                Logger.Log(String.Format(String.Format("Recognition status: {0}.\r\n", Result.Status.ToString())));

                if (1 == Result.Status)
                {
                    Logger.Log(String.Format("Fit-Circle Center: ({0}, {1}), Radius: {2}.\r\n"
                                                             , Result.Circle.Center.fX.ToString("#0.000")
                                                             , Result.Circle.Center.fY.ToString("#0.000")
                                                             , Result.Circle.Radius.ToString("#0.000")));
                    Logger.Log(String.Format("Fit-Arc Center: ({0}, {1}), Radius: {2}, StartAngle: {3}, AngleExtend: {4}.\r\n"
                                                             , Result.Arc.Center.fX.ToString("#0.000")
                                                             , Result.Arc.Center.fY.ToString("#0.000")
                                                             , Result.Arc.OuterRadius.ToString("#0.000")
                                                             , Result.Arc.StartAngle.ToString("#0.000")
                                                             , Result.Arc.AngleRange.ToString("#0.000")));
                    Logger.Log(String.Format("Fit-error: {0}.\r\n"
                                                             , Result.Circleness.ToString("#0.000")));

                    DisplayManager.DrawArc = new CMvdAnnularSectorF(Result.Arc);
                }

                // 绘制卡尺
                List<CMvdRectangleF> RectangleFList = new List<CMvdRectangleF>();
                foreach (var item in Result.CaliperBoxList)
                {
                    if (IsPointOutOfImage(new MVD_POINT_F(item.CenterX, item.CenterY), _InputImage.Width, _InputImage.Height))
                    {
                        continue;
                    }
                    CMvdRectangleF drawCaliperBox = new CMvdRectangleF(item);
                    drawCaliperBox.BorderColor = new MVD_COLOR(0, 0, 255, 255);
                    RectangleFList.Add(drawCaliperBox);
                }
                DisplayManager.DrawRectangleFList(RectangleFList);

                // 绘制轮廓点
                List<CMvdLineSegmentF> LineSegmentFList = new List<CMvdLineSegmentF>();
                foreach (var item in Result.EdgePointInfo)
                {
                    if (IsPointOutOfImage(item.EdgePoint, _InputImage.Width, _InputImage.Height))
                    {
                        continue;
                    }

                    CMvdLineSegmentF horizontalLine = null;
                    CMvdLineSegmentF verticalLine = null;
                    ScaleEdgePoint(item.EdgePoint
                                 , DisplayManager._Scale
                                 , out horizontalLine
                                 , out verticalLine);

                    switch (item.Status)
                    {
                        case MVD_EDGEPOINT_STATUS.MVD_EDGEPOINT_STATUS_USED:
                            {
                                horizontalLine.BorderColor = new MVD_COLOR(0, 255, 0, 255);
                                verticalLine.BorderColor = new MVD_COLOR(0, 255, 0, 255);
                            }
                            break;
                        case MVD_EDGEPOINT_STATUS.MVD_EDGEPOINT_STATUS_NO_USED:
                            {
                                horizontalLine.BorderColor = new MVD_COLOR(255, 255, 0, 255);
                                verticalLine.BorderColor = new MVD_COLOR(255, 255, 0, 255);
                            }
                            break;
                        case MVD_EDGEPOINT_STATUS.MVD_EDGEPOINT_STATUS_NO_FIND:
                            {
                                horizontalLine.BorderColor = new MVD_COLOR(255, 0, 0, 255);
                                verticalLine.BorderColor = new MVD_COLOR(255, 0, 0, 255);
                            }
                            break;
                        default:
                            break;
                    }
                    LineSegmentFList.Add(horizontalLine);
                    LineSegmentFList.Add(verticalLine);
                }
                DisplayManager.DrawOutline(LineSegmentFList);
            }
            catch (MvdException ex)
            {
                Logger.Log(String.Format("Fail to running the tool, Module : {0}, ErrorCode : 0x{1}, Message : {2}.\r\n", ex.ModuleType.ToString(), ex.ErrorCode.ToString("X"), ex.Message));
            }
            catch (System.Exception ex)
            {
                Logger.Log(String.Format("Fail to running the tool, Message : {0}, StackTrace : {1}.\r\n", ex.Message, ex.StackTrace));
            }
            finally
            {
                DisplayManager.mVDRender.Display();
            }
        }

        /// <summary>
        /// 点是否在图像外
        /// </summary>
        /// <param name="point"></param>
        /// <param name="imageWidth"></param>
        /// <param name="imageHeight"></param>
        /// <returns></returns>
        private bool IsPointOutOfImage(MVD_POINT_F point, float imageWidth, float imageHeight)
        {
            if ((point.fX - 0 < Single.Epsilon)
            || (point.fY - 0 < Single.Epsilon)
            || (imageWidth - point.fX < Single.Epsilon)
            || (imageHeight - point.fY < Single.Epsilon))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 单个轮廓点十字放大
        /// </summary>
        /// <param name="edgePoint"></param>
        /// <param name="scale"></param>
        /// <param name="imageSize"></param>
        /// <param name="horizontalLine"></param>
        /// <param name="verticalLine"></param>
        private void ScaleEdgePoint(MVD_POINT_F edgePoint, int scale, out CMvdLineSegmentF horizontalLine, out CMvdLineSegmentF verticalLine)
        {
            horizontalLine = new CMvdLineSegmentF(new MVD_POINT_F(), new MVD_POINT_F());
            verticalLine = new CMvdLineSegmentF(new MVD_POINT_F(), new MVD_POINT_F());

            float minX = edgePoint.fX - scale;
            float maxX = edgePoint.fX + scale;
            float minY = edgePoint.fY - scale;
            float maxY = edgePoint.fY + scale;

            horizontalLine.StartPoint = new MVD_POINT_F(minX, edgePoint.fY);
            horizontalLine.EndPoint = new MVD_POINT_F(maxX, edgePoint.fY);
            verticalLine.StartPoint = new MVD_POINT_F(edgePoint.fX, minY);
            verticalLine.EndPoint = new MVD_POINT_F(edgePoint.fX, maxY);
        }
        #endregion

        #region Action

        /// <summary>
        /// 设置圆查找参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ParamData_ValueChanged(object sender, ValueChangedEventArgs e)
        {
            try
            {
                string name = "";
                string value = "";
                if (e.ParamData is IntParamData intParamData)
                {
                    // 处理 IntParamData 特有的逻辑
                    name = intParamData.Name;
                    value = intParamData.Value.ToString();
                }
                else if (e.ParamData is EnumParamData enumParamData)
                {
                    // 处理 EnumParamData 特有的逻辑
                    name = enumParamData.Name;
                    value = enumParamData.ParamValue[enumParamData.Value];
                }

                _StopWatch.Restart();
                _CircleFindTool.SetRunParam(name, value);
                _StopWatch.Stop();
                Logger.Log(String.Format($"SetRunParam success. {e.ParamData.DisplayName} => {e.ParamData.Value} \r\n"));
            }
            catch (MvdException ex)
            {
                Logger.Log(String.Format("Fail to set run param, Module : {0}, ErrorCode : 0x{1}, Message : {2}.\r\n", ex.ModuleType.ToString(), ex.ErrorCode.ToString("X"), ex.Message));
            }
            catch (System.Exception ex)
            {
                Logger.Log(String.Format("Fail to set run param, Message : {0}, StackTrace : {1}.\r\n", ex.Message, ex.StackTrace));
            }
        }

        /// <summary>
        /// 点击运行事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CircleRun_Click(object sender, RoutedEventArgs e)
        {
            this.Run();
        }

        /// <summary>
        /// 导出Xml参数文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExportXml_Click(object sender, RoutedEventArgs e)
        {
            string filePath = FileDialogHelper.GetSaveFilePath();
            if (!string.IsNullOrEmpty(filePath))
            {
                SaveXmlFile(filePath);
            }
        }

        /// <summary>
        /// 导入Xml参数文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImportXml_Click(object sender, RoutedEventArgs e)
        {
            string filePath = FileDialogHelper.GetOpenFilePath();
            if (!string.IsNullOrEmpty(filePath))
            {
                ImportXmlFile(filePath);
            }
        }

        #endregion
    }
}
